package io.xxx.xbutler.sdk;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import io.xxx.xbutler.sdk.domain.Token;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
public class RedissonTokenProvider implements TokenProvider {

    private final RestTemplate restTemplate;

    private final RedissonClient redissonClient;

    public RedissonTokenProvider(RedissonClient redissonClient, RestTemplate restTemplate) {
        this.redissonClient = redissonClient;
        this.restTemplate = restTemplate;
    }

    @SneakyThrows
    @Override
    public Token getToken(String domain, String merchant, String secret) {
        String key = "tanjing:token:" + merchant;
        RBucket<String> bucket = redissonClient.getBucket(key);
        String tokenValue = bucket.get();
        if (StringUtils.hasText(tokenValue)) {
            return JSON.parseObject(tokenValue, Token.class);
        } else {
            RLock lock = redissonClient.getLock("lock:" + key);
            try {
                if (lock.tryLock()) {
                    Map<String, Object> params = Map.of(
                            "merchant", merchant,
                            "secret", secret
                    );
                    String url = UriComponentsBuilder.fromUriString(domain)
                            .path("/api/oauth/get_token")
                            .toUriString();
                    JSONObject response = restTemplate.postForObject(url, params, JSONObject.class);
                    if (response == null || response.getInteger("code") != 1) {
                        log.error("获取Token失败[params:{}]", JSON.toJSONString(params));
                        throw new RuntimeException("获取Token失败");
                    }
                    String value = response.getString("token");
                    LocalDateTime timestamp = LocalDateTime.ofInstant(response.getInstant("service_time"), ZoneId.systemDefault());
                    Token token = new Token(value, timestamp);
                    bucket.set(JSON.toJSONString(token));
                    bucket.expireIfNotSet(Duration.ofHours(2));
                    return token;
                } else {
                    int times = 0;
                    while (times++ < 5) {
                        TimeUnit.SECONDS.sleep(1);
                        String json = bucket.get();
                        if (StringUtils.hasText(json)) {
                            return JSON.parseObject(json, Token.class);
                        }
                    }
                    throw new RuntimeException("获取Token失败");
                }
            } finally {
                if (lock.isLocked()) {
                    lock.unlock();
                }
            }
        }
    }
}

